from typing import Optional
from application.model.UserModel import UserModel
from application.util.StatusCode import StatusCode
from application.util.RedisUtil import RedisUtil
from application.mapper.UserMapper import UserMapper
from application.exception.BasicException import BasicException
from application.util.StringUtil import random_uuid, md5_encode, is_valid_url, is_valid_password, is_valid_email
from application.util.TokenUtil import clear_token, get_user_id, generate_token


class UserLogic:
    """
    用户逻辑层
    """
    # 实例化redis客户端
    redis_client: RedisUtil = RedisUtil()

    @staticmethod
    def now_user_info(token: str) -> Optional[dict]:
        """
        获取当前用户信息
        :param token: Token
        :return:
        """
        user_id: str = get_user_id(token)
        user_model: Optional[UserModel] = UserMapper.get_info_by_id(user_id=user_id)
        if user_model is None:
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="用户不存在")
        user_info: dict = user_model.to_dict()
        user_info.pop("password")  # 删除密码字段
        return user_info

    @staticmethod
    def info_list() -> list:
        """
        获取用户信息列表
        :return:
        """
        result: list[Optional[dict]] = []
        for user_model in UserMapper.info_list():
            user_info: dict = user_model.to_dict()
            user_info.pop("password")  # 删除密码字段
            result.append(user_info)
        return result

    @classmethod
    def registered(cls, data: dict, valid_code: str) -> None:
        """
        注册/新增用户
        :param data: 用户信息字典
        :param valid_code: 邮箱验证码
        :return:
        """
        # 检查必填项
        if not all([data.get("username"), data.get("password"), data.get("nickname"), data.get("email"), valid_code]):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请完善内容")
        # 检查用户名是否已存在
        if UserMapper.get_info_by_username(username=data.get("username")):
            raise BasicException(status_code=StatusCode.ALREADY_EXIST_ERROR.value, error_message="该用户已存在")
        # 检查密码是否符合规范
        if not is_valid_password(text=str(data.get("password"))):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value,
                                 error_message="密码不符合规范（至少一个字母，至少一个数字，至少为6位）")
        # 检查邮箱格式
        if not is_valid_email(text=data.get("email")):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请输入正确邮箱")
        # 验证邮箱验证码
        cache_code: Optional[str] = cls.redis_client.get_value(key=data.get("email"))
        if not cache_code or cache_code != valid_code:
            raise BasicException(status_code=StatusCode.AUTHORITY_ERROR.value, error_message="邮箱验证码错误")
        # 删除Redis中的邮箱验证码
        cls.redis_client.delete_by_key(key=data.get("email"))
        data["password"] = md5_encode(text=str(data.get("password")))  # md5加密密码，先转为字符串
        user_model: UserModel = UserModel(**data)
        user_model.id = random_uuid()  # 随机生成ID
        if not UserMapper.insert(user_model=user_model):
            raise BasicException(status_code=StatusCode.ERROR.value, error_message="内部错误，请稍后再试")

    @staticmethod
    def login(data: dict) -> str:
        """
        登录
        :param data: 用户信息字典
        :return: Token
        """
        # 检查必填项
        if not all([data.get("username"), data.get("password")]):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请完善内容")
        # 检查密码是否符合规范
        if not is_valid_password(text=str(data.get("password"))):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value,
                                 error_message="密码不符合规范（至少一个字母，至少一个数字，至少为6位）")
        user_model: Optional[UserModel] = UserMapper.get_info_by_username(username=data.get("username"))
        # 检查用户是否存在
        if not user_model:
            raise BasicException(status_code=StatusCode.NOT_FOUND_ERROR.value, error_message="该用户不存在")
        # 检查密码是否正确
        if md5_encode(text=data.get("password")) != user_model.password:
            raise BasicException(status_code=StatusCode.AUTHORITY_ERROR.value, error_message="密码错误")
        return generate_token(user_id=user_model.id)

    @staticmethod
    def logout(token: str) -> None:
        """
        退出登录
        :param token: Token
        :return:
        """
        if not clear_token(token=token):  # 清空token
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="用户会话异常")

    @staticmethod
    def change_avatar(avatar_url: str, token: str) -> None:
        """
        修改头像
        :param avatar_url: 头像URL
        :param token: Token
        :return:
        """
        user_id: str = get_user_id(token=token)
        user_model: Optional[UserModel] = UserMapper.get_info_by_id(user_id=user_id)
        # 检查用户是否存在
        if not user_model:
            raise BasicException(status_code=StatusCode.NOT_FOUND_ERROR.value, error_message="该用户不存在")
        # 检查是否为URL
        if not avatar_url or not is_valid_url(text=avatar_url):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请输入正确URL")
        # 修改头像
        if not UserMapper.update_by_id(user_id=user_id, update_dict={"avatar": avatar_url}):
            raise BasicException(status_code=StatusCode.ERROR.value, error_message="内部错误，请稍后再试")

    @staticmethod
    def change_password(old_password: str, new_password: str, token: str) -> None:
        """
        修改密码
        :param old_password: 旧密码
        :param new_password: 新密码
        :param token: Token
        :return:
        """
        user_id: str = get_user_id(token=token)
        user_model: Optional[UserModel] = UserMapper.get_info_by_id(user_id=user_id)
        # 检查用户是否存在
        if not user_model:
            raise BasicException(status_code=StatusCode.NOT_FOUND_ERROR.value, error_message="该用户不存在")
        # 检查是否为空
        if not all([old_password, new_password]):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请完善内容")
        # 检查密码是否正确
        if user_model.password != md5_encode(text=old_password):
            raise BasicException(status_code=StatusCode.AUTHORITY_ERROR.value, error_message="旧密码错误")
        # 检查密码是否符合规范
        if not is_valid_password(text=new_password):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value,
                                 error_message="密码不符合规范（至少一个字母，至少一个数字，至少为6位）")
        # 修改密码
        if not UserMapper.update_by_id(user_id=user_id, update_dict={"password": md5_encode(text=new_password)}):
            raise BasicException(status_code=StatusCode.ERROR.value, error_message="内部错误，请稍后再试")

    @staticmethod
    def change_nickname(nickname: str, token: str) -> None:
        """
        修改昵称
        :param nickname: 昵称
        :param token: Token
        :return:
        """
        user_id: str = get_user_id(token=token)
        user_model: Optional[UserModel] = UserMapper.get_info_by_id(user_id=user_id)
        # 检查用户是否存在
        if not user_model:
            raise BasicException(status_code=StatusCode.NOT_FOUND_ERROR.value, error_message="该用户不存在")
        # 检查是否为空
        if not nickname:
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请完善内容")
        # 修改昵称
        if not UserMapper.update_by_id(user_id=user_id, update_dict={"nickname": nickname}):
            raise BasicException(status_code=StatusCode.ERROR.value, error_message="内部错误，请稍后再试")

    @classmethod
    def change_email(cls, email: str, valid_code: str, token: str) -> None:
        """
        修改邮箱
        :param email: 邮箱
        :param valid_code: 邮箱验证码
        :param token: Token
        :return:
        """
        user_id: str = get_user_id(token=token)
        user_model: Optional[UserModel] = UserMapper.get_info_by_id(user_id=user_id)
        # 检查用户是否存在
        if not user_model:
            raise BasicException(status_code=StatusCode.NOT_FOUND_ERROR.value, error_message="该用户不存在")
        # 检查必填项
        if not all([email, valid_code]):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请完善内容")
        # 检查邮箱格式
        if not is_valid_email(text=email):
            raise BasicException(status_code=StatusCode.BAD_REQUEST_ERROR.value, error_message="请输入正确邮箱")
        # 验证邮箱验证码
        cache_code: Optional[str] = cls.redis_client.get_value(key=email)
        if not cache_code or cache_code != valid_code:
            raise BasicException(status_code=StatusCode.AUTHORITY_ERROR.value, error_message="邮箱验证码错误")
        # 删除Redis中的邮箱验证码
        cls.redis_client.delete_by_key(key=email)
        # 修改邮箱
        if not UserMapper.update_by_id(user_id=user_id, update_dict={"email": email}):
            raise BasicException(status_code=StatusCode.ERROR.value, error_message="内部错误，请稍后再试")
